package fudan.se.project.security;

import fudan.se.project.exception.infrastructure.AccessDeniedExceptionHandler;
import fudan.se.project.exception.infrastructure.AuthenticationExceptionHandler;
import fudan.se.project.security.filter.ConferenceScheduleFilter;
import fudan.se.project.security.filter.JwtRequestFilter;
import fudan.se.project.security.permisson.UserPermissionEvaluator;
import fudan.se.project.service.infrastructure.base.*;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author Gao Longchao
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @NonNull
    private final JwtRequestFilter jwtRequestFilter;
    @NonNull
    private final ConferenceScheduleFilter conferenceScheduleFilter;
    @NonNull
    private final UserAuthenticationService userAuthenticationService;
    @NonNull
    private final ConferencePermissionService conferencePermissionService;
    @NonNull
    private final PaperPermissionService paperPermissionService;
    @NonNull
    private final ReviewInfoPermissionService reviewInfoPermissionService;
    @NonNull
    private final PostPermissionService postPermissionService;
    @NonNull
    private final AuthenticationExceptionHandler authenticationExceptionHandler;
    @NonNull
    private final AccessDeniedExceptionHandler accessDeniedExceptionHandler;
    @NonNull
    private final PasswordEncoder encoder;

    @Bean
    public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new UserPermissionEvaluator(
                conferencePermissionService,
                paperPermissionService,
                reviewInfoPermissionService, postPermissionService));
        return handler;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userAuthenticationService).passwordEncoder(encoder);
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/login", "/register").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(conferenceScheduleFilter, JwtRequestFilter.class);
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationExceptionHandler)
                .accessDeniedHandler(accessDeniedExceptionHandler);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/h2-console/**");
    }


}
